{
 "metadata": {
  "signature": "sha256:c4f2a236a0640ab885d3149f74c266a53589b14c7b69846b0701381cfcc1a750"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "markdown",
     "id": "880214AB1C5648F38B538583312A0D86",
     "metadata": {},
     "source": [
      "1 Black-Litterman\u6a21\u578b\u6982\u8ff0\n",
      "\u57fa\u4e8e\u9a6c\u79d1\u7ef4\u8328\u5747\u503c-\u65b9\u5dee\u6a21\u578b\u7684\u8d44\u4ea7\u7ec4\u5408\u5206\u6790\u9700\u8981\u83b7\u53d6\u5404\u7c7b\u8d44\u4ea7\u9884\u671f\u6536\u76ca\u548c\u65b9\u5dee\u3002\u901a\u5e38\u6709\u4e24\u79cd\u65b9\u6cd5\u53ef\u4ee5\u7528\u6765\u5f97\u5230\u9884\u671f\u6536\u76ca\u548c\u6536\u76ca\u7387\u65b9\u5dee\u7684\u4f30\u8ba1\uff0c\u60c5\u666f\u5206\u6790\u6cd5\u548c\u5386\u53f2\u6570\u636e\u6cd5\u3002\u60c5\u666f\u5206\u6790\u6cd5\u4e3b\u8981\u6839\u636e\u5f53\u524d\u884c\u60c5\u548c\u5b8f\u89c2\u7ecf\u6d4e\u73af\u5883\u7b49\u56e0\u7d20\u5f62\u6210\u4e3b\u89c2\u7684\u9884\u671f\uff0c\u8fd9\u79cd\u65b9\u6cd5\u663e\u7136\u4e3b\u89c2\u6027\u3001\u968f\u610f\u6027\u8fc7\u5f3a\u3002\u5386\u53f2\u6570\u636e\u6cd5\u5219\u5b8c\u5168\u6839\u636e\u8fc7\u53bb\u7684\u5386\u53f2\u6536\u76ca\u7387\u8ba1\u7b97\u6536\u76ca\u5747\u503c\u548c\u65b9\u5dee\uff0c\u7528\u6765\u4ee3\u66ff\u5bf9\u672a\u6765\u7684\u9884\u671f\uff0c\u8fd9\u4e5f\u662f\u76ee\u524d\u4e3b\u6d41\u7684\u505a\u6cd5\u3002\u8fd9\u79cd\u505a\u6cd5\u5b58\u5728\u51e0\u4e2a\u95ee\u9898\uff0c\u4e00\u662f\u5386\u53f2\u6570\u636e\u5f80\u5f80\u7531\u4e8e\u5386\u53f2\u7684\u4e00\u4e9b\u5b8f\u89c2\u73af\u5883\u6216\u968f\u673a\u56e0\u7d20\u800c\u5b58\u5728\u6bd4\u8f83\u5927\u7684\u6ce2\u52a8\u6027\uff0c\u8fd9\u4e5f\u610f\u5473\u7740\u5386\u53f2\u7684\u6536\u76ca\u7387\u548c\u65b9\u5dee\u5728\u672a\u6765\u6709\u53ef\u80fd\u7531\u4e8e\u5b8f\u89c2\u73af\u5883\u548c\u968f\u673a\u56e0\u7d20\u7684\u6539\u53d8\u800c\u4e0d\u4f1a\u91cd\u6f14\uff1b\u4e8c\u662f\u6839\u636e\u91c7\u53d6\u7684\u5386\u53f2\u6570\u636e\u7684\u65f6\u95f4\u6bb5\u4e0d\u540c\uff0c\u4f30\u7b97\u51fa\u7684\u9884\u671f\u6536\u76ca\u7387\u548c\u65b9\u5dee\u4e5f\u4f1a\u6709\u8f83\u5927\u5dee\u522b\uff0c\u4ece\u800c\u5bfc\u81f4\u5f97\u51fa\u7684\u6700\u4f18\u8d44\u4ea7\u914d\u7f6e\u6bd4\u4f8b\u4e5f\u4f1a\u6709\u8f83\u5927\u5dee\u522b\u3002\u9ad8\u76db\u7684Black F.\u548cLitterman R.\u5728\u51761991\u5e74\u7684\u4e00\u7bc7\u8bba\u6587\u4e2d\u63d0\u5230\uff0c\u5728\u5bf9\u5168\u7403\u503a\u5238\u6295\u8d44\u7ec4\u5408\u7684\u7814\u7a76\u4e2d\uff0c\u4ed6\u4eec\u53d1\u73b0\uff0c\u5f53\u5bf9\u5fb7\u56fd\u503a\u5238\u9884\u671f\u62a5\u916c\u7387\u505a0.1%\u5c0f\u5e45\u4fee\u6b63\u540e\uff0c\u8be5\u7c7b\u8d44\u4ea7\u7684\u6295\u8d44\u6bd4\u4f8b\u7adf\u7531\u539f\u6765\u768410.0%\u63d0\u9ad8\u81f355.0%\u3002\u8fd9\u4e5f\u610f\u5473\u7740\u9a6c\u79d1\u7ef4\u8328\u7684\u5747\u503c-\u65b9\u5dee\u6a21\u578b\u5f97\u5230\u7684\u6295\u8d44\u7ec4\u5408\u5bf9\u4e8e\u8f93\u5165\u7684\u53c2\u6570\u8fc7\u4e8e\u654f\u611f\u3002 \n",
      "Black\u548cLitteraman\u5728\u524d\u8ff0\u5747\u503c\u65b9\u5dee\u6a21\u578b\u7684\u57fa\u7840\u4e0a\uff0c\u901a\u8fc7\u5386\u53f2\u6570\u636e\u4f30\u8ba1\u57fa\u51c6\u9884\u671f\u548c\u65b9\u5dee\uff0c\u5bfc\u5165\u6295\u8d44\u8005\u4e3b\u89c2\u9884\u671f\uff0c\u628a\u5386\u53f2\u6570\u636e\u6cd5\u548c\u60c5\u666f\u5206\u6790\u6cd5\u7ed3\u5408\u8d77\u6765\uff0c\u5f62\u6210\u65b0\u7684\u5e02\u573a\u6536\u76ca\u9884\u671f\uff0c\u4ece\u800c\u89e3\u51b3\u4e86\u524d\u8ff0\u6a21\u578b\u4e2d\u9884\u671f\u6536\u76ca\u548c\u65b9\u5dee\u4f30\u8ba1\u4e2d\u5b58\u5728\u7684\u95ee\u9898\u3002\n",
      "2 Black-Litterman\u6a21\u578b\u7b80\u4ecb\n",
      "B-L\u6a21\u578b\u5728\u5747\u8861\u6536\u76ca\u57fa\u7840\u4e0a\u901a\u8fc7\u6295\u8d44\u8005\u89c2\u70b9\u4fee\u6b63\u4e86\u671f\u671b\u6536\u76ca\uff0c\u4f7f\u5f97\u5747\u503c\u65b9\u5dee\u7ec4\u5408\u4f18\u5316\u4e2d\u7684\u671f\u671b\u6536\u76ca\u66f4\u4e3a\u5408\u7406\uff0c\u800c\u4e14\u8fd8\u5c06\u6295\u8d44\u8005\u89c2\u70b9\u878d\u5165\u8fdb\u4e86\u6a21\u578b\uff0c\u5728\u4e00\u5b9a\u7a0b\u5ea6\u4e0a\u662f\u5bf9\u9a6c\u79d1\u7ef4\u8328\u5747\u503c\u65b9\u5dee\u7ec4\u5408\u7406\u8bba\u7684\u6539\u8fdb\u3002\n",
      "B-L\u6a21\u578b\u5047\u8bbe\u5404\u8d44\u4ea7\u6536\u76ca\u7387R\u670d\u4ece\u8054\u5408\u6b63\u6001\u5206\u5e03\uff0cR~N(\u03bc,\u03a3)\uff0c\u5176\u4e2d\u03bc\u548c\u03a3\u662f\u5404\u8d44\u4ea7\u9884\u671f\u6536\u76ca\u7387\u548c\u534f\u65b9\u5dee\u7684\u4f30\u8ba1\u503c\u3002\u73b0\u5728\u5047\u8bbe\u4f30\u8ba1\u5411\u91cf\u03bc\u672c\u8eab\u4e5f\u662f\u968f\u673a\u7684\uff0c\u4e14\u670d\u4ece\u6b63\u6001\u5206\u5e03\uff1a\u03bc~N(\u03c0,\u03c4\u03a3)\uff0c\u5176\u4e2d\u03c0\u4e3a\u5148\u9a8c\u671f\u671b\u6536\u76ca\u7387\u7684\u671f\u671b\u503c\uff0c\u901a\u5e38\u7531\u5386\u53f2\u5e73\u5747\u6536\u76ca\u7387\u8868\u793a\u3002\u6a21\u578b\u5f15\u5165\u6295\u8d44\u8005\u4e2a\u4eba\u89c2\u70b9\u7684\u65b9\u5f0f\u662f\u7528\u7ebf\u6027\u65b9\u7a0b\u7ec4\u8868\u793a\uff0c\u6bcf\u4e00\u4e2a\u65b9\u7a0b\u8868\u793a\u4e00\u4e2a\u89c2\u70b9\u3002\u4f8b\u5982\uff0c\u6295\u8d44\u8005\u8ba4\u4e3a\u672a\u6765\u7b2c\u4e09\u79cd\u8d44\u4ea7\u4f1a\u6bd4\u7b2c\u4e00\u79cd\u8d44\u4ea7\u6536\u76ca\u7387\u9ad82%\uff0c\u5c31\u53ef\u4ee5\u8868\u793a\u4e3a\uff1a\n",
      "-1\u00d7\u03bc_1+0\u00d7\u03bc_2+1\u00d7\u03bc_3+\u22ef+0\u00d7\u03bc_N=2%\n",
      "\u6295\u8d44\u8005\u8ba4\u4e3a\u672a\u6765\u7b2c\u4e8c\u79cd\u8d44\u4ea7\u6536\u76ca\u7387\u5e94\u8be5\u4e3a5%\uff0c\u90a3\u4e48\u53ef\u4ee5\u8868\u793a\u4e3a\uff1a\n",
      "0\u00d7\u03bc_1+1\u00d7\u03bc_2+0\u00d7\u03bc_3+\u22ef+0\u00d7\u03bc_N=5%\n",
      "\u7528P\u6765\u8868\u793a\u8be5\u89c2\u70b9\u7ebf\u6027\u65b9\u7a0b\u7ec4\u7684\u7cfb\u6570\u77e9\u9635\uff0c\u89c2\u70b9\u65b9\u7a0b\u7ec4\u53ef\u8868\u793a\u4e3a\uff1aP\u03bc = q\u3002\u7531\u4e8e\u6295\u8d44\u4eba\u89c2\u70b9\u4e5f\u5b58\u5728\u4e0d\u786e\u5b9a\u6027\uff0c\u56e0\u6b64\u5728q\u7684\u57fa\u7840\u4e0a\u8fd8\u53ef\u4ee5\u52a0\u4e0a\u4e00\u4e2a\u968f\u673a\u8bef\u5dee\u9879\uff1aP\u03bc = q+ \u03f5\uff0c\u5176\u4e2d\u03f5~N(0, \u03a9)\uff0c\u56e0\u800cP\u03bc~N(q, \u03a9)\u3002\u8fd9\u91cc\uff0cP\u88ab\u79f0\u4e3aPick Matrix\uff0c\u4e3aK\u00d7N\u77e9\u9635\uff0c\u8868\u793a\u5bf9\u4e8eN\u79cd\u8d44\u4ea7\u7684K\u4e2a\u89c2\u70b9\uff1bq\u4e3aK\u00d71\u770b\u6cd5\u5411\u91cf\uff1b\u03a9\u4e3a\u770b\u6cd5\u5411\u91cf\u8bef\u5dee\u9879\u7684K\u00d7K\u534f\u65b9\u5dee\u77e9\u9635\uff0c\u8868\u793a\u6295\u8d44\u8005\u89c2\u70b9\u7684\u4e0d\u786e\u5b9a\u7a0b\u5ea6\u3002\u901a\u5e38\u5bf9\u4e8e\u03a9\uff0c\u91c7\u7528\u03a9=diag(\u03c4P\u03a3P^T)\u7684\u65b9\u5f0f\u6784\u9020\u3002\n",
      "\u524d\u9762\u6211\u4eec\u8ba8\u8bba\u8fc7\u5e02\u573a\u7684\u770b\u6cd5\uff1a\u03bc~N(\u03c0,\u03c4\u03a3)\uff0c\u7528P\u8c03\u6574\u540e\u7684\u5e02\u573a\u770b\u6cd5\u53ef\u8868\u793a\u4e3a\uff1aP\u03bc~N(P\u03c0,\u03c4P\u03a3P^T)\u3002\n",
      "\u6295\u8d44\u4eba\u89c2\u70b9\u548c\u5e02\u573a\u770b\u6cd5\u7684\u5dee\u8ddd\u670d\u4ece\u5206\u5e03\uff1aN(q-P\u03c0, \u03a9+\u03c4P\u03a3P^T)\n",
      "\u7136\u540e\u6839\u636e\u8d1d\u53f6\u65af\u6cd5\u5219\uff0c\u7ed3\u5408\u5148\u9a8c\u4fe1\u606f\u548c\u6295\u8d44\u8005\u89c2\u70b9\uff0c\u53ef\u4ee5\u8ba1\u7b97\u8c03\u6574\u540e\u7684\u9884\u671f\u6536\u76ca\u7387\u548c\u6536\u76ca\u7387\u65b9\u5dee\u5206\u522b\u4e3a\uff1a\n",
      "\u03a0 \u0302=\u03a0+\u03c4\u03a3P^T \u3016(\u03a9+\u03c4P\u03a3P^T)\u3017^(-1) (q-P\u03c0)\n",
      "M=\u03c4\u03a3-\u03c4\u03a3P^T \u3016(\u03a9+\u03c4P\u03a3P^T)\u3017^(-1) P\u03c4\u03a3\n",
      "\u03a3_P=\u03a3+M\n",
      "\u5176\u4e2d\uff0c\u03a0\u4e3a\u5148\u9a8c\u7684\u671f\u671b\u6536\u76ca\uff0c\u901a\u8fc7\u5386\u53f2\u5e73\u5747\u5e74\u5316\u6536\u76ca\u5f97\u5230\uff1b\u03a0 \u0302\u4e3a\u4e2a\u4eba\u89c2\u70b9\u8c03\u6574\u540e\u7684\u671f\u671b\u6536\u76ca\uff1b\u03a3\u4e3a\u8d44\u4ea7\u6536\u76ca\u7387\u4e4b\u95f4\u7684\u534f\u65b9\u5dee\u77e9\u9635\uff1bM\u4e3a\u540e\u9a8c\u5206\u5e03\u9884\u671f\u6536\u76ca\u7387\u7684\u65b9\u5dee\uff1b\u03a3_P\u4e3a\u8c03\u6574\u540e\u9884\u671f\u6536\u76ca\u7387\u65b9\u5dee\uff1b\u03c4\u4e3a\u5747\u8861\u6536\u76ca\u65b9\u5dee\u7684\u523b\u5ea6\u503c\uff0c\u4f53\u73b0\u4e86\u5bf9\u4e2a\u4eba\u89c2\u70b9\u5728\u603b\u4f53\u4f30\u8ba1\u4e2d\u7684\u6743\u91cd\uff0c\u901a\u5e38\u53d6\u503c\u57280.025~0.05\uff1b P\u3001q\u3001\u03a9\u4e3a\u89c2\u70b9\u77e9\u9635\u3002\n",
      "\u5f97\u5230\u8c03\u6574\u540e\u7684\u671f\u671b\u6536\u76ca\u7387\u548c\u65b9\u5dee\u540e\uff0c\u5c31\u53ef\u4ee5\u6839\u636e\u9a6c\u79d1\u7ef4\u8328\u5747\u503c\u65b9\u5dee\u6a21\u578b\u8ba1\u7b97\u6700\u4f18\u6743\u91cd\u3002\n",
      "3 Black-Litterman\u6a21\u578b\u5e94\u7528\n",
      "\u5728\u5b9e\u8df5\u4e2d\u5e94\u7528\u8be5\u6a21\u578b\uff0c\u7b80\u8981\u6765\u8bf4\u4e3b\u8981\u6709\u5982\u4e0b\u6b65\u9aa4\uff1a\n",
      "(1) \u8ba1\u7b97\u5f97\u5230\u5148\u9a8c\u7684\u671f\u671b\u6536\u76ca\uff1b\n",
      "(2) \u4e2a\u4eba\u89c2\u70b9\u6a21\u578b\u5316\uff0c\u89c2\u70b9\u53ef\u4ee5\u6d89\u53ca\u5355\u4e2a\u8d44\u4ea7\uff0c\u4e5f\u53ef\u4ee5\u6709\u591a\u4e2a\u8d44\u4ea7\uff0c\u6700\u540e\u6309\u7167\u4e00\u5b9a\u7684\u89c4\u5219\u5c06\u6240\u6709\u89c2\u70b9\u6784\u5efa\u6210\u77e9\u9635P\u3001Q\u548c\u03a9\uff1b\n",
      "(3) \u8ba1\u7b97\u8c03\u6574\u540e\u7684\u9884\u671f\u6536\u76ca\u7387\u3001\u8c03\u6574\u540e\u7684\u6536\u76ca\u7387\u65b9\u5dee\uff1b\n",
      "(4) \u6839\u636e\u8c03\u6574\u540e\u7684\u671f\u671b\u6536\u76ca\uff0c\u5229\u7528\u5747\u503c\u65b9\u5dee\u6a21\u578b\u8ba1\u7b97\u6700\u4f18\u6743\u91cd\u3002\n",
      "\u4e0b\u9762\u7684\u4ee3\u7801\u63d0\u4f9b\u4e86\u82e5\u5e72\u5e94\u7528B-L\u6a21\u578b\u8fdb\u884c\u4f18\u5316\u7684\u51fd\u6570\u3002\u5176\u4e2dget_BL_efficient_frontier()\u7528\u4e8e\u83b7\u53d6\u6839\u636e\u8c03\u6574\u540e\u7684\u6536\u76ca\u7387\u671f\u671b\u548c\u65b9\u5dee\u5f97\u5230\u7684\u6709\u6548\u8fb9\u754c\uff1bdraw_efficient_frontier()\u7528\u4e8e\u7ed8\u5236\u6709\u6548\u8fb9\u754c\u56fe\u5f62\uff1bget_BL_minimum_variance_portfolio()\u7528\u4e8e\u83b7\u53d6\u6700\u5c0f\u65b9\u5dee\u6295\u8d44\u7ec4\u5408\uff1bget_BL_maximum_utility_portfolio()\u7528\u4e8e\u83b7\u53d6\u57fa\u4e8e\u7ed9\u5b9a\u6548\u7528\u51fd\u6570\u7684\u6700\u5927\u5316\u6548\u7528\u6295\u8d44\u7ec4\u5408\uff1bget_maximum_sharpe_portfolio()\u7528\u4e8e\u83b7\u53d6\u6700\u5927\u590f\u666e\u7387\u6295\u8d44\u7ec4\u5408\u3002\n"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "365BF491F29C430E885FE8361B17E69A",
     "input": [
      "def describe(return_table, is_print=True):\n",
      "    \"\"\"\n",
      "    \u8f93\u51fa\u6536\u76ca\u7387\u77e9\u9635\u7684\u63cf\u8ff0\u6027\u7edf\u8ba1\u91cf\uff0c\u5305\u62ec\uff1a\n",
      "        \u5e74\u5316\u6536\u76ca\u7387\n",
      "        \u5e74\u5316\u6807\u51c6\u5dee\n",
      "        \u76f8\u5173\u7cfb\u6570\u77e9\u9635\n",
      "    \n",
      "    Args:\n",
      "        return_table (DataFrame): \u6536\u76ca\u7387\u77e9\u9635\uff0c\u5217\u4e3a\u8d44\u4ea7\uff0c\u503c\u4e3a\u6309\u65e5\u671f\u5347\u5e8f\u6392\u5217\u7684\u6536\u76ca\u7387\n",
      "        is_print (bool): \u662f\u5426\u76f4\u63a5\u8f93\u51fa\n",
      "\n",
      "    Returns:\n",
      "        dict: \u63cf\u8ff0\u6027\u7edf\u8ba1\u91cf\u5b57\u5178\uff0c\u952e\u4e3a\"annualized_return\", \"annualized_volatility\", \"covariance_matrix\"\u548c\"coefficient_matrix\"\n",
      "\n",
      "    Examples:\n",
      "        >> describe(return_table)\n",
      "        >> describe(return_table, is_print=True)\n",
      "    \"\"\"\n",
      "    import numpy as np\n",
      "    import pandas as pd\n",
      "    from scipy.stats.mstats import gmean\n",
      "    \n",
      "    output = {}\n",
      "    output['annualized_return'] = pd.DataFrame(dict(zip(return_table.columns, gmean(return_table+1.)**252 - 1.)), index=[0], columns=return_table.columns)\n",
      "    output['annualized_volatility'] = pd.DataFrame(return_table.std() * np.sqrt(250)).T\n",
      "    output['covariance_matrix'] = return_table.cov() * 250.\n",
      "    output['coefficient_matrix'] = return_table.corr()\n",
      "        \n",
      "    if is_print:\n",
      "        for key, val in output.iteritems():\n",
      "            print \"{}:\\n{}\\n\".format(key, val)\n",
      "    \n",
      "    return output\n",
      "\n",
      "\n",
      "def get_BL_efficient_frontier(return_table,tau=0.05,P=None,Q=None,Omega=None,allow_short=False, n_samples=25):\n",
      "    \"\"\"\n",
      "    \u8ba1\u7b97Efficient Frontier\n",
      "    \n",
      "    Args:\n",
      "        return_table (DataFrame): \u6536\u76ca\u7387\u77e9\u9635\uff0c\u5217\u4e3a\u8d44\u4ea7\uff0c\u503c\u4e3a\u6309\u65e5\u671f\u5347\u5e8f\u6392\u5217\u7684\u6536\u76ca\u7387\n",
      "        n_samples (int): \u7528\u4e8e\u8ba1\u7b97Efficient Frontier\u7684\u91c7\u6837\u70b9\u6570\u91cf\n",
      "        P(np.array): \u89c2\u70b9\u77e9\u9635\n",
      "        Q(np.array): \u89c2\u70b9\u6536\u76ca\u77e9\u9635\n",
      "        Omega(np.array): \u89c2\u70b9\u7f6e\u4fe1\u5ea6\u77e9\u9635\n",
      "        tau(float): \u4e3a\u5747\u8861\u6536\u76ca\u65b9\u5dee\u7684\u523b\u5ea6\u503c\uff0c\u4f53\u73b0\u4e86\u5bf9\u4e2a\u4eba\u89c2\u70b9\u5728\u603b\u4f53\u4f30\u8ba1\u4e2d\u7684\u6743\u91cd\n",
      "\n",
      "    Returns:\n",
      "        DataFrame: Efficient Frontier\u7684\u7ed3\u679c\uff0c\u5217\u4e3a\"returns\", \"risks\", \"weights\"\n",
      "    \"\"\"\n",
      "    \n",
      "    import numpy as np\n",
      "    import pandas as pd\n",
      "    from cvxopt import matrix, solvers\n",
      "    \n",
      "    assets = return_table.columns\n",
      "    n_asset = len(assets)\n",
      "    if n_asset < 2:\n",
      "        raise ValueError(\"There must be at least 2 assets to calculate the efficient frontier!\")\n",
      "\n",
      "    output = describe(return_table, is_print=False)\n",
      "    covmat =(output['covariance_matrix'])\n",
      "    expected_return = output['annualized_return'].iloc[0, :]\n",
      "    \n",
      "    # \u6c42\u89e3\u8c03\u6574\u540e\u7684\u671f\u671b\u6536\u76ca\u3001\u65b9\u5dee\n",
      "    adjustedReturn = expected_return + tau*covmat.dot(P.transpose()).dot(np.linalg.inv(Omega+tau*(P.dot(covmat).dot(P.transpose())))).dot(Q - P.dot(expected_return))\n",
      "    right = (tau)*covmat.dot(P.transpose()).dot(np.linalg.inv(Omega+P.dot(covmat).dot(P.transpose()))).dot(P.dot(tau*covmat))\n",
      "    right = right.transpose()\n",
      "    right = right.set_index(expected_return.index)\n",
      "    M = tau*covmat - right\n",
      "    Sigma_p = covmat + M\n",
      "    adjustedReturn = adjustedReturn.as_matrix()\n",
      "    Sigma_p = matrix(Sigma_p.as_matrix())\n",
      "\t\n",
      "    risks, returns, weights = [], [], []\n",
      "    for level_return in np.linspace(min(adjustedReturn), max(adjustedReturn), n_samples):\n",
      "        P = 2 * Sigma_p\n",
      "        q = matrix(np.zeros(n_asset))\n",
      "        \n",
      "        if allow_short:\n",
      "            G = matrix(0., (n_asset, n_asset))\n",
      "        else:\n",
      "            G = matrix(np.diag(-1 * np.ones(n_asset)))\n",
      "        \n",
      "        h = matrix(0., (n_asset, 1))    \n",
      "        A = matrix(np.row_stack((np.ones(n_asset), adjustedReturn)))\n",
      "        b = matrix([1.0, level_return])\n",
      "        solvers.options['show_progress'] = False\n",
      "        sol = solvers.qp(P, q, G, h, A, b)\n",
      "        risks.append(np.sqrt(sol['primal objective']))\n",
      "        returns.append(level_return)\n",
      "        weights.append(dict(zip(assets, list(sol['x'].T))))\n",
      "    \n",
      "    output = {\"returns\": returns,\n",
      "              \"risks\": risks,\n",
      "              \"weights\": weights}\n",
      "    output = pd.DataFrame(output)\n",
      "    return output\n",
      "\n",
      "def draw_efficient_frontier(effcient_frontier_output):\n",
      "    \"\"\"\n",
      "    \u7ed8\u51faEfficient Frontier\n",
      "    \n",
      "    Args:\n",
      "        effcient_frontier_output: Efficient Frontier\u7684\u8ba1\u7b97\u7ed3\u679c\uff0c\u5373get_efficient_frontier\u7684\u8f93\u51fa\n",
      "    \"\"\"\n",
      "\n",
      "    import seaborn\n",
      "    from matplotlib import pyplot as plt\n",
      "\n",
      "    fig = plt.figure(figsize=(7, 4))\n",
      "    ax = fig.add_subplot(111)\n",
      "    ax.plot(effcient_frontier_output['risks'], effcient_frontier_output['returns'])\n",
      "    ax.set_title('Efficient Frontier', fontsize=14)\n",
      "    ax.set_xlabel('Standard Deviation', fontsize=12)\n",
      "    ax.set_ylabel('Expected Return', fontsize=12)\n",
      "    ax.tick_params(labelsize=12)\n",
      "    plt.show()\n",
      "\n",
      "def get_BL_minimum_variance_portfolio(return_table,tau=0.05,P=None,Q=None,Omega=None, allow_short=False, show_details=True):\n",
      "    \"\"\"\n",
      "    \u8ba1\u7b97\u6700\u5c0f\u65b9\u5dee\u7ec4\u5408\n",
      "    \n",
      "    Args:\n",
      "        return_table (DataFrame): \u6536\u76ca\u7387\u77e9\u9635\uff0c\u5217\u4e3a\u8d44\u4ea7\uff0c\u503c\u4e3a\u6309\u65e5\u671f\u5347\u5e8f\u6392\u5217\u7684\u6536\u76ca\u7387\n",
      "        allow_short (bool): \u662f\u5426\u5141\u8bb8\u5356\u7a7a\n",
      "        show_details (bool): \u662f\u5426\u663e\u793a\u7ec6\u8282\n",
      "        P(np.array): \u89c2\u70b9\u77e9\u9635\n",
      "        Q(np.array): \u89c2\u70b9\u6536\u76ca\u77e9\u9635\n",
      "        Omega(np.array): \u89c2\u70b9\u7f6e\u4fe1\u5ea6\u77e9\u9635\n",
      "        tau(float): \u4e3a\u5747\u8861\u6536\u76ca\u65b9\u5dee\u7684\u523b\u5ea6\u503c\uff0c\u4f53\u73b0\u4e86\u5bf9\u4e2a\u4eba\u89c2\u70b9\u5728\u603b\u4f53\u4f30\u8ba1\u4e2d\u7684\u6743\u91cd\n",
      "\n",
      "    Returns:\n",
      "        dict: \u6700\u5c0f\u65b9\u5dee\u7ec4\u5408\u7684\u6743\u91cd\u4fe1\u606f\uff0c\u952e\u4e3a\u8d44\u4ea7\u540d\uff0c\u503c\u4e3a\u6743\u91cd\n",
      "    \"\"\"\n",
      "    \n",
      "    import numpy as np\n",
      "    from cvxopt import matrix, solvers\n",
      "    \n",
      "    assets = return_table.columns\n",
      "    n_asset = len(assets)\n",
      "    if n_asset < 2:\n",
      "        weights = np.array([1.])\n",
      "        weights_dict = {assets[0]: 1.}\n",
      "    else:\n",
      "        output = describe(return_table, is_print=False)\n",
      "        covmat =(output['covariance_matrix'])\n",
      "        expected_return = output['annualized_return'].iloc[0, :]\n",
      "    \n",
      "        # \u6c42\u89e3\u8c03\u6574\u540e\u7684\u671f\u671b\u6536\u76ca\u3001\u65b9\u5dee\n",
      "        adjustedReturn = expected_return + tau*covmat.dot(P.transpose()).dot(np.linalg.inv(Omega+tau*(P.dot(covmat).dot(P.transpose())))).dot(Q - P.dot(expected_return))\n",
      "        right = (tau)*covmat.dot(P.transpose()).dot(np.linalg.inv(Omega+P.dot(covmat).dot(P.transpose()))).dot(P.dot(tau*covmat))\n",
      "        right = right.transpose()\n",
      "        right = right.set_index(expected_return.index)\n",
      "        M = tau*covmat - right\n",
      "        Sigma_p = covmat + M\n",
      "        adjustedReturn = adjustedReturn.as_matrix()\n",
      "        Sigma_p = matrix(Sigma_p.as_matrix())\n",
      "\n",
      "        P = 2 * Sigma_p\n",
      "        q = matrix(np.zeros(n_asset))\n",
      "\n",
      "        if allow_short:\n",
      "            G = matrix(0., (n_asset, n_asset))\n",
      "        else:\n",
      "            G = matrix(np.diag(-1 * np.ones(n_asset)))\n",
      "        \n",
      "        h = matrix(0., (n_asset, 1))\n",
      "        A = matrix(np.ones(n_asset)).T\n",
      "        b = matrix([1.0])\n",
      "        solvers.options['show_progress'] = False\n",
      "        sol = solvers.qp(P, q, G, h, A, b)\n",
      "        weights = np.array(sol['x'].T)[0]\n",
      "        weights_dict = dict(zip(assets, weights))\n",
      "\n",
      "    r = np.dot(weights, output['annualized_return'].iloc[0, :].as_matrix())\n",
      "    v = np.sqrt(np.dot(np.dot(weights, Sigma_p), weights.T))\n",
      "\n",
      "    if show_details:\n",
      "        print \"\"\"\n",
      "Minimum Variance Portfolio:\n",
      "    Short Allowed: {}\n",
      "    Portfolio Return: {}\n",
      "    Portfolio Volatility: {}\n",
      "    Portfolio Weights: {}\n",
      "\"\"\".format(allow_short, r, v, \"\\n\\t{}\".format(\"\\n\\t\".join(\"{}: {:.1%}\".format(k, v) for k, v in weights_dict.items()))).strip()\n",
      "    \n",
      "    return weights_dict\n",
      "\n",
      "def get_BL_maximum_utility_portfolio(return_table,tau=0.05,P=None,Q=None,Omega=None, risk_aversion=3., allow_short=False, show_details=True):\n",
      "    \"\"\"\n",
      "    \u8ba1\u7b97\u6700\u5927\u6548\u7528\u7ec4\u5408\uff0c\u76ee\u6807\u51fd\u6570\u4e3a\uff1a\u671f\u671b\u5e74\u5316\u6536\u76ca\u7387 - \u98ce\u9669\u538c\u6076\u7cfb\u6570 * \u671f\u671b\u5e74\u5316\u65b9\u5dee\n",
      "    \n",
      "    Args:\n",
      "        return_table (DataFrame): \u6536\u76ca\u7387\u77e9\u9635\uff0c\u5217\u4e3a\u8d44\u4ea7\uff0c\u503c\u4e3a\u6309\u65e5\u671f\u5347\u5e8f\u6392\u5217\u7684\u6536\u76ca\u7387\n",
      "        risk_aversion (float): \u98ce\u9669\u538c\u6076\u7cfb\u6570\uff0c\u8d8a\u5927\u8868\u793a\u5bf9\u98ce\u9669\u8d8a\u538c\u6076\uff0c\u9ed8\u8ba4\u4e3a3.0\n",
      "        allow_short (bool): \u662f\u5426\u5141\u8bb8\u5356\u7a7a\n",
      "        show_details (bool): \u662f\u5426\u663e\u793a\u7ec6\u8282\n",
      "        P(np.array): \u89c2\u70b9\u77e9\u9635\n",
      "        Q(np.array): \u89c2\u70b9\u6536\u76ca\u77e9\u9635\n",
      "        Omega(np.array): \u89c2\u70b9\u7f6e\u4fe1\u5ea6\u77e9\u9635\n",
      "        tau(float): \u4e3a\u5747\u8861\u6536\u76ca\u65b9\u5dee\u7684\u523b\u5ea6\u503c\uff0c\u4f53\u73b0\u4e86\u5bf9\u4e2a\u4eba\u89c2\u70b9\u5728\u603b\u4f53\u4f30\u8ba1\u4e2d\u7684\u6743\u91cd\n",
      "\n",
      "    Returns:\n",
      "        dict: \u6700\u5c0f\u65b9\u5dee\u7ec4\u5408\u7684\u6743\u91cd\u4fe1\u606f\uff0c\u952e\u4e3a\u8d44\u4ea7\u540d\uff0c\u503c\u4e3a\u6743\u91cd\n",
      "    \"\"\"\n",
      "    \n",
      "    import numpy as np\n",
      "    from cvxopt import matrix, solvers\n",
      "\n",
      "    assets = return_table.columns\n",
      "    n_asset = len(assets)\n",
      "    if n_asset < 2:\n",
      "        weights = np.array([1.])\n",
      "        weights_dict = {assets[0]: 1.}\n",
      "    else:\n",
      "        output = describe(return_table, is_print=False)\n",
      "        covmat =(output['covariance_matrix'])\n",
      "        expected_return = output['annualized_return'].iloc[0, :]\n",
      "    \n",
      "        # \u6c42\u89e3\u8c03\u6574\u540e\u7684\u671f\u671b\u6536\u76ca\u3001\u65b9\u5dee\n",
      "        adjustedReturn = expected_return + tau*covmat.dot(P.transpose()).dot(np.linalg.inv(Omega+tau*(P.dot(covmat).dot(P.transpose())))).dot(Q - P.dot(expected_return))\n",
      "        right = (tau)*covmat.dot(P.transpose()).dot(np.linalg.inv(Omega+P.dot(covmat).dot(P.transpose()))).dot(P.dot(tau*covmat))\n",
      "        right = right.transpose()\n",
      "        right = right.set_index(expected_return.index)\n",
      "        M = tau*covmat - right\n",
      "        Sigma_p = covmat + M\n",
      "        adjustedReturn = adjustedReturn.as_matrix()\n",
      "        Sigma_p = matrix(Sigma_p.as_matrix())\n",
      "\n",
      "        if abs(risk_aversion) < 0.01:\n",
      "            max_ret = max(adjustedReturn)\n",
      "            weights = np.array([1. if adjustedReturn[i] == max_ret else 0. for i in range(n_asset)])\n",
      "            weights_dict = {asset: weights[i] for i, asset in enumerate(assets)}\n",
      "        else:\n",
      "            P = risk_aversion * Sigma_p\n",
      "            q = matrix(-adjustedReturn.T)\n",
      "\n",
      "            if allow_short:\n",
      "                G = matrix(0., (n_asset, n_asset))\n",
      "            else:\n",
      "                G = matrix(np.diag(-1 * np.ones(n_asset)))\n",
      "\n",
      "            h = matrix(0., (n_asset, 1))\n",
      "            A = matrix(np.ones(n_asset)).T\n",
      "            b = matrix([1.0])\n",
      "            solvers.options['show_progress'] = False\n",
      "            sol = solvers.qp(P, q, G, h, A, b)\n",
      "            weights = np.array(sol['x'].T)[0]\n",
      "            weights_dict = dict(zip(assets, weights))\n",
      "\n",
      "    r = np.dot(weights, output['annualized_return'].iloc[0, :].as_matrix())\n",
      "    v = np.sqrt(np.dot(np.dot(weights, Sigma_p), weights.T))\n",
      "    \n",
      "    if show_details:\n",
      "        print \"\"\"\n",
      "Maximum Utility Portfolio:\n",
      "    Risk Aversion: {}\n",
      "    Short Allowed: {}\n",
      "    Portfolio Return: {}\n",
      "    Portfolio Volatility: {}\n",
      "    Portfolio Weights: {}\n",
      "\"\"\".format(risk_aversion, allow_short, r, v, \"\\n\\t{}\".format(\"\\n\\t\".join(\"{}: {:.1%}\".format(k, v) for k, v in weights_dict.items()))).strip()\n",
      "    \n",
      "    return weights_dict\n",
      "\n",
      "def get_maximum_sharpe_portfolio(return_table, riskfree_rate=0.,tau=0.05,P=None,Q=None,Omega=None,allow_short=False, show_details=True):\n",
      "    \"\"\"\n",
      "    \u8ba1\u7b97\u6700\u5927\u6548\u7528\u7ec4\u5408\uff0c\u76ee\u6807\u51fd\u6570\u4e3a\uff1a\uff08\u671f\u671b\u5e74\u5316\u6536\u76ca\u7387 - \u65e0\u98ce\u9669\u6536\u76ca\u7387\uff09/ \u671f\u671b\u5e74\u5316\u65b9\u5dee\n",
      "    \n",
      "    Args:\n",
      "        return_table (DataFrame): \u6536\u76ca\u7387\u77e9\u9635\uff0c\u5217\u4e3a\u8d44\u4ea7\uff0c\u503c\u4e3a\u6309\u65e5\u671f\u5347\u5e8f\u6392\u5217\u7684\u6536\u76ca\u7387\n",
      "        riskfree_rate (float): \u65e0\u98ce\u9669\u6536\u76ca\u7387\n",
      "        allow_short (bool): \u662f\u5426\u5141\u8bb8\u5356\u7a7a\n",
      "        show_details (bool): \u662f\u5426\u663e\u793a\u7ec6\u8282\n",
      "        P(np.array): \u89c2\u70b9\u77e9\u9635\n",
      "        Q(np.array): \u89c2\u70b9\u6536\u76ca\u77e9\u9635\n",
      "        Omega(np.array): \u89c2\u70b9\u7f6e\u4fe1\u5ea6\u77e9\u9635\n",
      "        tau(float): \u4e3a\u5747\u8861\u6536\u76ca\u65b9\u5dee\u7684\u523b\u5ea6\u503c\uff0c\u4f53\u73b0\u4e86\u5bf9\u4e2a\u4eba\u89c2\u70b9\u5728\u603b\u4f53\u4f30\u8ba1\u4e2d\u7684\u6743\u91cd\n",
      "\n",
      "    Returns:\n",
      "        dict: \u6700\u5c0f\u65b9\u5dee\u7ec4\u5408\u7684\u6743\u91cd\u4fe1\u606f\uff0c\u952e\u4e3a\u8d44\u4ea7\u540d\uff0c\u503c\u4e3a\u6743\u91cd\n",
      "    \"\"\"\n",
      "    \n",
      "    import numpy as np\n",
      "    from cvxopt import matrix, solvers\n",
      "\n",
      "    assets = return_table.columns\n",
      "    n_asset = len(assets)\n",
      "    if n_asset < 2:\n",
      "        output = describe(return_table, is_print=False)\n",
      "        r = output['annualized_return'].iat[0, 0]\n",
      "        v = output['annualized_volatility'].iat[0, 0]\n",
      "        weights_dict = {assets[0]: 1.}\n",
      "    else:\n",
      "        efs = get_BL_efficient_frontier(return_table,tau,P=P,Q=Q,Omega=Omega,allow_short=allow_short, n_samples=100)\n",
      "        i_star = max(range(100), key=lambda x: (efs.at[x, \"returns\"] - riskfree_rate) / efs.at[x, \"risks\"])\n",
      "        r = efs.at[i_star, \"returns\"]\n",
      "        v = efs.at[i_star, \"risks\"]\n",
      "        weights_dict = efs.at[i_star, \"weights\"]\n",
      "\n",
      "    s = (r - riskfree_rate) / v\n",
      "    \n",
      "    if show_details:\n",
      "        print \"\"\"\n",
      "Maximum Sharpe Portfolio:\n",
      "    Riskfree Rate: {}\n",
      "    Short Allowed: {}\n",
      "    Portfolio Return: {}\n",
      "    Portfolio Volatility: {}\n",
      "    Portfolio Sharpe: {}\n",
      "    Portfolio Weights: {}\n",
      "\"\"\".format(riskfree_rate, allow_short, r, v, s, \"\\n\\t{}\".format(\"\\n\\t\".join(\"{}: {:.1%}\".format(k, v) for k, v in weights_dict.items()))).strip()\n",
      "    \n",
      "    return weights_dict\n"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "markdown",
     "id": "E415764F107C4E7D845A317DBAFD14F8",
     "metadata": {},
     "source": [
      "\u4e0b\u9762\uff0c\u6211\u4eec\u5229\u7528\u6caa\u6df1300\u6307\u6570\u3001\u4e2d\u8bc1500\u6307\u6570\u3001\u521b\u4e1a\u677f\u6307\u3001\u56fd\u503a\u6307\u6570\u3001\u4f01\u4e1a\u503a\u6307\u6570\u6784\u9020\u4e00\u4e2a\u5229\u7528B-L\u6a21\u578b\u7684\u7b80\u5355\u793a\u4f8b\u3002"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "1A0D2FDCB0DF4C199819A2ADC29B1175",
     "input": [
      "import numpy as np\n",
      "import pandas as pd\n",
      "from cvxopt import matrix, solvers\n",
      "start = '20130101'\n",
      "end = '20160630'\n",
      "indices = ['000300.ZICN', '000905.ZICN', '399006.ZICN', '000012.ZICN','000013.ZICN']\n",
      "df = DataAPI.MktIdxdGet(indexID=indices, beginDate=start, endDate=end, field=\"tradeDate,indexID,closeIndex\")\n",
      "df = df.pivot(index=\"tradeDate\", columns=\"indexID\", values=\"closeIndex\")\n",
      "for index in indices:\n",
      "    df[index] = df[index] / df[index].shift() - 1.\n",
      "return_table = df.dropna()\n",
      "\n",
      "describe(return_table, is_print=False)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "80F2749531B0407C828774BA0D7CE352",
     "input": [
      "# - \u751f\u6210\u89c2\u70b9\u77e9\u9635P\u3001Q\u3001Omega\uff0c\u6211\u4eec\u77e5\u9053\u5386\u53f2\u6570\u636e\u7531\u4e8e\u5305\u542b\u4e8614-15\u725b\u5e02\uff0c\u5386\u53f2\u5e73\u5747\u6536\u76ca\u504f\u9ad8\uff0c\u5c24\u5176\u521b\u4e1a\u677f\uff0c\u5bf9\u4e8e\u521a\u7ecf\u53863\u8f6e\u80a1\u707e\u7684\u73b0\u5728\uff0c\u663e\u7136\u8981\u8c03\u6574\u80a1\u5e02\u7684\u671f\u671b\u6536\u76ca\u3002\u7ed9\u51fa\u4e0b\u97623\u4e2a\u89c2\u70b9\uff1a\n",
      "# \t- \u89c2\u70b91\uff1a \u4e2d\u8bc1500\u7684\u6536\u76ca\u7387\u8d85\u8fc7\u6caa\u6df1300\u6536\u76ca5%(\u76f8\u6bd4\u539f\u6765\u5dee\u8ddd\u7684\u8fd120%\u663e\u8457\u4e0b\u8c03)\n",
      "# \t- \u89c2\u70b92\uff1a HS300\u7684\u6536\u76ca\u7387\u9ad8\u4e8e\u56fd\u503a1%\uff0c\uff08\u76f8\u5bf9\u539f\u6765\u5dee\u8ddd\u5fae\u8c03\uff09\n",
      "# \t- \u89c2\u70b93\uff1a\u521b\u4e1a\u677f\u6307\u6536\u76ca\u6bd4\u4f01\u4e1a\u503a\u6307\u6570\u9ad85%\uff08\u76f8\u6bd4\u539f\u6765\u5dee\u8ddd\u8fd133%\u663e\u8457\u4e0b\u8c03\uff09\n",
      "output = describe(return_table, is_print=False)\n",
      "covariance_matrix = output['covariance_matrix']\n",
      "expected_return = output['annualized_return'].iloc[0, :]\n",
      "tau = 0.05 \n",
      "\n",
      "P = np.array([[-1,1,0,0,0],[1,0,0,-1,0],[0,0,1,0,-1]])\n",
      "print \"P:\",P\n",
      "Q = np.array([0.05,0.01,0.05])\n",
      "print \"Q:\",Q\n",
      "Omega = tau*(P.dot(covariance_matrix).dot(P.transpose()))\n",
      "Omega = np.diag(np.diag(Omega,k=0))\n",
      "print \"Omega:\",Omega"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "DAFA055A04C54739861D3DBE8C303F8A",
     "input": [
      "efficient_frontier = get_BL_efficient_frontier(return_table,tau,P=P,Q=Q,Omega=Omega,allow_short=False, n_samples=50)\n",
      "draw_efficient_frontier(efficient_frontier)\n",
      "efficient_frontier.head()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "7735E312B5AC4B12B6B2709EDD002741",
     "input": [
      "get_BL_minimum_variance_portfolio(return_table,tau=0.05,P=P,Q=Q,Omega=Omega, allow_short=False, show_details=True)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "93DB128ABB024A559D8DB96F51983741",
     "input": [
      "get_BL_maximum_utility_portfolio(return_table,tau=0.05,P=P,Q=Q,Omega=Omega, risk_aversion=3., allow_short=False, show_details=True)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "id": "8791DFA1E67D411483E9D6F77F5BC4FA",
     "input": [
      "get_maximum_sharpe_portfolio(return_table, riskfree_rate=0.,tau=0.05,P=P,Q=Q,Omega=Omega,allow_short=True, show_details=True)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}